import asyncio
import aio_pika
from config import RABBITMQ_URI


async def fanout_consumer(queue_name: str, consumer_id: int):
    """
    Fanout Exchange 消费者：监听单个队列，接收 Fanout 广播的消息
    :param queue_name: 要监听的队列名称（与生产者 setup 中创建的队列一致）
    :param consumer_id: 消费者标识，区分不同队列的消费者
    """
    # 1. 建立稳健连接（自动重连机制，应对网络波动或 RabbitMQ 重启）
    connection = await aio_pika.connect_robust(RABBITMQ_URI)
    # 2. 创建通信信道（所有消息操作通过信道执行，减少 TCP 连接开销）
    channel = await connection.channel()

    # 3. 开启公平调度：确保消费者处理完 1 条消息后再接收下 1 条
    # 避免单队列内消息堆积（尤其 Fanout 场景下多队列并行处理需均衡）
    await channel.set_qos(prefetch_count=1)

    # 4. 声明要监听的队列（与生产者 setup_fanout_exchange 中创建的队列完全一致）
    # 若队列未初始化（未执行 setup），会报错提醒先完成交换器和队列创建
    queue = await channel.declare_queue(
        queue_name,
        durable=True,  # 与生产者一致：队列持久化（重启不丢失）
        auto_delete=False  # 队列不自动删除（即使无消费者也保留，确保广播消息不丢失）
    )

    # 5. 定义消息处理逻辑（Fanout 场景下，同一消息会被所有队列的消费者接收）
    async def on_message_received(message: aio_pika.IncomingMessage):
        # async with 上下文：自动确认消息（处理完成后告知 RabbitMQ 删除，避免重复消费）
        # 若处理崩溃，消息会重新入队，等待消费者重启后重试
        async with message.process():
            # 解码消息体（生产者用 utf-8 编码，此处对应解码）
            message_content = message.body.decode("utf-8")
            # Print key information (clearly showing Fanout broadcast feature: same message received by multiple queues)
            print(f"[Fanout Consumer {consumer_id}] Received broadcast message:")
            print(f"  Listening queue: {queue_name}")
            print(f"  Message content: {message_content}")
            print(f"  Message persistence: {'Yes' if message.delivery_mode == 2 else 'No'}")  # Verify persistence
            # print(f"  处理时间：{asyncio.get_running_loop().time():.2f}s\n")

            # 模拟业务处理耗时（根据实际场景替换，如日志存储、通知推送等）
            await asyncio.sleep(1)

    # 6. 启动队列监听：将处理逻辑绑定到队列，持续接收广播消息
    consumer_tag = f"fanout_consumer_{consumer_id}_{queue_name}"
    await queue.consume(on_message_received, consumer_tag=consumer_tag)
    # Print startup log, indicating consumer is ready
    print(f"[Fanout Consumer {consumer_id}] Started, listening to queue: {queue_name} (tag: {consumer_tag})\n")

    # 7. 保持消费者运行（无限期阻塞，直到手动停止程序）
    # 若不阻塞，协程会立即结束，消费者连接会断开
    await asyncio.Future()


async def start_all_fanout_consumers(queue_prefix="demo.fanout.queue-", queue_count=3):
    """
    启动所有 Fanout 消费者：与生产者 setup 中创建的 3 个队列一一对应
    :param queue_prefix: 队列名称前缀（与生产者 queue_name_prefix 一致）
    :param queue_count: 队列数量（与生产者中 range(3) 对应，默认 3 个）
    """
    # 构建消费者任务列表：为每个队列创建一个独立消费者
    consumer_tasks = [
        fanout_consumer(
            queue_name=f"{queue_prefix}{i}",  # 队列名：demo.fanout.queue-0/1/2（与生产者一致）
            consumer_id=i + 1  # 消费者标识：1/2/3
        )
        for i in range(queue_count)
    ]

    # 并发启动所有消费者（3 个消费者同时运行，互不阻塞）
    await asyncio.gather(*consumer_tasks)


if __name__ == "__main__":
    # 启动所有 Fanout 消费者（需先执行 setup_fanout_exchange 初始化交换器和队列）
    asyncio.run(start_all_fanout_consumers())
